package com.heima.comment.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.heima.apis.user.IUserClient;
import com.heima.comment.dtos.CommentLikeDto;
import com.heima.comment.dtos.CommentListDto;
import com.heima.comment.dtos.CommentSaveDto;
import com.heima.comment.pojos.ApComment;
import com.heima.comment.pojos.ApCommentLike;
import com.heima.comment.service.CommentService;
import com.heima.comment.vo.ApCommentVo;
import com.heima.common.aliyun.GreenTextScan;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.user.pojos.ApUser;
import com.heima.utils.common.ApThreadLocalUtil;

import org.bson.types.ObjectId;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class CommentServiceImpl implements CommentService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private GreenTextScan textScan;

    @Autowired
    private IUserClient userClient;

    /**
     * 保存评论列表
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult save(CommentSaveDto dto) {
        //1.先检查参数
        if (dto.getArticleId() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        if (dto.getContent() == null && dto.getContent().length() > 140) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE, "评论内容不能超过140字");
        }

        //2.获取登录的用户,,并判断是否登录
        ApUser apUser = ApThreadLocalUtil.getUser();

        //2.1判断是否登录
        if (apUser == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }
        //2.1判断是否是游客登录
        if (apUser.getId() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN, "请登录之后再来评论！！");
        }
        //3.查询redis中的数据,如果不存在再从用户微服务中去查询
        String key = redisTemplate.opsForValue().get("LOGIN_USER_" + apUser.getId());
        //3.1//如果为空，则表示第一次,则远程从用户服务中调取
        if (StringUtils.isBlank(key)) {
            ResponseResult<ApUser> result = userClient.findUserById(apUser.getId().longValue());
            if (result.getCode() == 200 && result.getData() != null) {
                apUser = result.getData();
                String toJSONString = JSON.toJSONString(apUser);
                //存入redis中,并设置两个小时过期时间
                redisTemplate.opsForValue().set("LOGIN_USER_" + apUser.getId(), toJSONString, 2, TimeUnit.HOURS);
            }
        } else {
            //表式不止第一次登录
            apUser = JSON.parseObject(key, ApUser.class);
        }
        //4.对评论信息进行安全过滤
        Boolean textFilterFlag = scanTextFilter(dto.getContent());
        if (!textFilterFlag) {
            return ResponseResult.errorResult(AppHttpCodeEnum.COMMENT_BLOCK);
        }
        //5.审核通过后保存数据到mongo中
        ApComment apComment = new ApComment();
        BeanUtils.copyProperties(apUser, apComment);
        apComment.setTargetId(dto.getContent());
        apComment.setCreatedTime(new Date());
        apComment.setUpdatedTime(new Date());
        apComment.setId(ObjectId.get().toHexString());
        apComment.setContent(dto.getContent());
        apComment.setLikes(0);
        apComment.setReply(0);
        mongoTemplate.insert(apComment);
        return ResponseResult.okResult("评论成功");
    }

    /**
     * 评论点赞
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult like(CommentLikeDto dto) {
        //1.参数检查
        if (dto.getCommentId() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        //2.获取登录用户，并判断是否登录
        ApUser apUser = ApThreadLocalUtil.getUser();
        if (apUser == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }
        //判断是否是游客登录，是则禁止操作
        if (apUser.getId() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN, "请登录之后再来点赞！！");
        }
        //3.判断当前评论是否存在，查询MongoDB，不存在就返回
        ApComment apComment = mongoTemplate.findById(dto.getCommentId(), ApComment.class);
        if (apComment == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //4.查询redis中是否点赞过
        String LIKE_KEY = "COMMENT_LIKE_" + apUser.getId();
        //表示根据当前key下的所有的value中是否包含某一个，如果包含返回true,否则false
        Boolean aBoolean = redisTemplate.opsForSet().isMember(LIKE_KEY, dto.getCommentId());
        //4.1 如果点赞过，并且现在正在执行的是点赞操作，直接返回
        if (aBoolean && dto.getOperation() == 0) {
            return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
        }
        //4.2 如若点赞过，并执行取消点赞操作
        if (aBoolean && dto.getOperation() == 1) {
            //删除redis中数据
            redisTemplate.opsForSet().remove(LIKE_KEY, dto.getCommentId());
            //删除MongoDB中数据
            mongoTemplate.remove(
                    Query.query(Criteria.where("userId").is(apUser.getId()).and("targetId").is(dto.getCommentId()))
                    , ApCommentLike.class);
            //然后修改mongo中的评论集合，点赞数-1
            mongoTemplate.updateFirst(Query.query(Criteria.where("id").is(apComment.getId())
                            .and("likes").gt(0))
                    , new Update().inc("likes", -1)
                    , ApComment.class
            );
        }
        //5.如果没有点赞过，并且正在执行的是点赞操作，
        if (!aBoolean && dto.getOperation() == 0) {
            // 新增redis
            redisTemplate.opsForSet().add(LIKE_KEY, dto.getCommentId());
            // 新增mongo中的点赞集合
            ApCommentLike apCommentLike = new ApCommentLike();
            apCommentLike.setId(ObjectId.get().toHexString());
            apCommentLike.setUserId(apUser.getId());
            apCommentLike.setCreatedTime(new Date());
            apCommentLike.setTargetId(dto.getCommentId());
            mongoTemplate.insert(apCommentLike);
            // 修改mongo中的评论集合，点赞数+1
            mongoTemplate.updateFirst(
                    Query.query(Criteria.where("id").is(apComment.getId()))
                    , new Update().inc("likes", 1)
                    , ApComment.class
            );
        }
        //6.返回数据
        apComment = mongoTemplate.findById(apComment.getId(), ApComment.class);
        Map map = new HashMap();
        map.put("likes", apComment.getLikes());
        return ResponseResult.okResult(map);
    }


    /**
     * 加载评论列表
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult findByArticleId(CommentListDto dto) {
        //参数检查
        if (dto.getArticleId() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        if (dto.getSize() == null || dto.getSize() == 0) {
            dto.setSize(10);
        }
        dto.setSize(Math.min(dto.getSize(), 50));
        //2根据文章id去查询mongo的评论表，并进行分页和排序展示
        List<ApComment> commentList = mongoTemplate.find(
                Query.query(Criteria.where("targetId").is(dto.getArticleId())
                        .and("createdTime").lt(dto.getMinDate()))
                        .with(Sort.by(Sort.Direction.DESC, "createdTime"))
                        .limit(dto.getSize()), ApComment.class);
        if (commentList == null || commentList.size() < 0) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //3.如果此刻是游客登录的话，则直接返回评论列表
        ApUser apUser = ApThreadLocalUtil.getUser();
        if (apUser.getId() == null) {
            return ResponseResult.okResult(commentList);
        }
        //4.否则就是用户登录，加载数据的时候，要判断当前用户点赞了哪些评论，并给予标识
        //4.1 查询redis中是否有点赞数据
        String key="COMMENT_LIKE_"+apUser.getId();
        Set<String> members = redisTemplate.opsForSet().members(key);
        //4.2 如果没有查询到数据,则直接返回评论列表
        if (members == null) {
            return ResponseResult.okResult(commentList);
        }
        //4.3 否则，就是存在点赞数据，则添加标记
        List<ApCommentVo> commentVoList = commentList.stream().map(comment ->
        {
            ApCommentVo vo = new ApCommentVo();
            BeanUtils.copyProperties(comment, vo);
            if (key.contains(comment.getId())) {
                vo.setOperation((short) 0);
            }
            return vo;
        }).collect(Collectors.toList());
        //5. 返回数据
        return ResponseResult.okResult(commentVoList);
    }

    /**
     * 数据安全检查
     *
     * @param content
     * @return
     */
    private Boolean scanTextFilter(String content) {
        Boolean flag = true;
        try {
            Map map = textScan.greeTextScan(content);

            //没通过审核
            if (map != null && !map.get("suggestion").equals("pass")) {
                flag = false;
            }
        } catch (Exception e) {
            e.printStackTrace();
            flag = false;
        }
        //通过审核
        return flag;
    }
}
